package cn.lg.soar.common.algorithm;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import static cn.lg.soar.common.util.data.DataUtil.BASE64DE;
import static cn.lg.soar.common.util.data.DataUtil.BASE64EN;

/**
 * 非对称加密工具
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class RSAUtils {

    public static final String RSA = "RSA";
    private static volatile Key staticKey;
    private static volatile RSAPublicKeyCoder publicKeyCoder;
    private static volatile RSAPrivateKeyCoder privateKeyCoder;

    /**
     * 加密
     * @param publicKey 公钥
     * @param data 数据
     * @return
     */
    public static byte[] encrypt(byte[] publicKey, byte[] data) {
        try {
            PublicKey key = KeyFactory.getInstance(RSAUtils.RSA)
                    .generatePublic(new X509EncodedKeySpec(publicKey));
            Cipher cipher = Cipher.getInstance(RSAUtils.RSA);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(data);
        } catch (IllegalBlockSizeException | InvalidKeySpecException | BadPaddingException |NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException e) {
            throw new AlgorithmException(e);
        }
    }

    /**
     * 解密
     * @param privateKey 私钥
     * @param data 数据
     * @return
     */
    public static byte[] decrypt(byte[] privateKey, byte[] data) {
        try {
            PrivateKey key = KeyFactory.getInstance(RSAUtils.RSA)
                    .generatePrivate(new PKCS8EncodedKeySpec(privateKey));
            Cipher cipher = Cipher.getInstance(RSAUtils.RSA);
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(data);
        } catch (IllegalBlockSizeException | InvalidKeySpecException | BadPaddingException |NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException e) {
            throw new AlgorithmException(e);
        }
    }

    /**
     * 加密
     * @param data 数据
     * @return
     */
    public String encryptToBase64(byte[] publicKey, byte[] data) {
        return BASE64EN.encodeToString(encrypt(publicKey, data));
    }

    /**
     * 解密
     * @param data 数据
     * @return
     */
    public byte[] decryptOfBase64(byte[] privateKey, String data) {
        return decrypt(privateKey, BASE64DE.decode(data));
    }

    /**
     * 获取编码器
     * @return
     */
    public static RSAPublicKeyCoder publicKeyCoder() {
        if (null == RSAUtils.publicKeyCoder) {
            RSAUtils.publicKeyCoder = new RSAPublicKeyCoder(RSAUtils.getStaticKey().getPublicKey());
        }
        return RSAUtils.publicKeyCoder;
    }

    /**
     * 获取公钥编码器
     * @param publicKey 公钥
     * @return
     */
    public static RSAPublicKeyCoder publicKeyCoder(byte[] publicKey) {
        return new RSAPublicKeyCoder(publicKey);
    }

    /**
     * 获取私钥编码器
     * @return
     */
    public static RSAPrivateKeyCoder privateKeyCoder() {
        if (null == RSAUtils.privateKeyCoder) {
            RSAUtils.privateKeyCoder = new RSAPrivateKeyCoder(RSAUtils.getStaticKey().getPrivateKey());
        }
        return RSAUtils.privateKeyCoder;
    }

    /**
     * 获取私钥编码器
     * @param privateKey 私钥
     * @return
     */
    public static RSAPrivateKeyCoder privateKeyCoder(byte[] privateKey) {
        return new RSAPrivateKeyCoder(privateKey);
    }

    /**
     * 构建密钥对
     * @return 构建完的公钥私钥
     * @throws NoSuchAlgorithmException
     */
    public static Key createKey(int keySize) throws NoSuchAlgorithmException {
        KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance(RSAUtils.RSA);
        senderKeyPairGenerator.initialize(keySize);
        KeyPair keyPair = senderKeyPairGenerator.generateKeyPair();
        return new Key(keyPair.getPublic().getEncoded(), keyPair.getPrivate().getEncoded());
    }

    /**
     * 构建密钥对
     * @return 构建完的公钥私钥
     * @throws NoSuchAlgorithmException
     */
    public static Key createKey() throws NoSuchAlgorithmException {
        return RSAUtils.createKey(1024);
    }

    /**
     * 获取公钥
     * @return
     */
    public static byte[] getPublicKey() {
        return RSAUtils.getStaticKey().getPublicKey();
    }

    /**
     * 获取私钥
     * @return
     */
    public static byte[] getPrivateKey() {
        return RSAUtils.getStaticKey().getPrivateKey();
    }

    private static Key getStaticKey() {
        if (RSAUtils.staticKey == null) {
            synchronized (RSAUtils.class) {
                if (RSAUtils.staticKey == null) {
                    try {
                        RSAUtils.staticKey = RSAUtils.createKey(1024);
                    } catch (NoSuchAlgorithmException e) {
                        throw new AlgorithmException(e);
                    }
                }
            }
        }
        return RSAUtils.staticKey;
    }

    private RSAUtils() {}

    public static class Key {

        private final byte[] privateKey;

        private final byte[] publicKey;

        private Key(byte[] publicKey, byte[] privateKey){
            this.privateKey = privateKey;
            this.publicKey = publicKey;
        }

        public byte[] getPrivateKey() {
            return this.privateKey;
        }

        public byte[] getPublicKey() {
            return this.publicKey;
        }

    }
}
